package main

import (
	"fmt"
	"reflect"
)

type monster struct {
	Name   string `json:"name"`
	Age    int    `json:"age"`
	Attack float64
}

func (m *monster) Speaking() {
	fmt.Printf("My name is %v,I am %v years old,I will Take your life.\n", m.Name, m.Age)
}

func (m *monster) MakeAttack() {
	fmt.Printf("%v hit you fast and hard, and you lost %v health\n", m.Name, m.Attack)
}

func (m *monster) GetSum(a, b int) int {
	return a + b
}

func test(b interface{}) {
	rTyp := reflect.TypeOf(b)
	rVal := reflect.ValueOf(b)
	fmt.Println(rTyp, rVal)
	//rVal可以还原成接口再断言，也可以直接用b接口参数
	//binterface := rVal.Interface()
	if _, ok := b.(*monster); ok {
		num := rVal.Elem().NumField()
		for i := 0; i < num; i++ {
			tagVal := rTyp.Elem().Field(i).Tag.Get("json")
			fmt.Printf("字段%v,json序列化%v    ", rTyp.Elem().Field(i).Name, tagVal)
		}
		fmt.Println()
		fmt.Println("方法个数为:", rVal.NumMethod())
		rVal.Method(2).Call(nil)
		rVal.Method(1).Call(nil)

		//v.attack = v.attack + 50,可以直接通过接口断言之后返回的结构体指针直接操作结构体
		//下面是利用反射的方法操作结构体
		rVal.Elem().Field(2).SetFloat(60)
		rVal.Elem().FieldByName("Age").SetInt(500)
		fmt.Println(rVal.Elem().Field(2).Float(), rVal.Elem().FieldByName("Age"))
		rVal.Method(1).Call(nil)

		//call调用方法传参和接收参数都是[]reflect.Value
		para := []reflect.Value{reflect.ValueOf(10), reflect.ValueOf(20)}
		sum := rVal.Method(0).Call(para)
		fmt.Printf("%v--%T--%v--%T--%d", sum, sum, sum[0], sum[0], len(sum))
	}
}

func main() {
	//test("hello")
	//test(123)
	greenCow := monster{"greenCow", 350, 15.5}
	test(&greenCow)
	fmt.Println()
	test1()
}

type Student struct {
	Name   string `json:"name" cn:"名字"`
	Age    uint64 `json:"age" cn:"年龄"`
	Gender bool   `json:"gender"`
	level  int
}

func test1() {
	b := 15
	s := Student{
		Name:   "zhansam",
		Age:    18,
		Gender: false,
		level:  2,
	}
	sType := reflect.TypeOf(s)
	for i := 0; i < sType.NumField(); i++ {
		fieldType := sType.Field(i)
		fmt.Printf("属性名:%v, 字段是否可导出:%v, tag:%v, struct中第%v位,>>> tag长度: %v\n", fieldType.Name, fieldType.IsExported(), fieldType.Tag, fieldType.Index, len(fieldType.Tag))
	}
	//首字母大写也必须用对应的取值方法
	IntVal := reflect.ValueOf(&b)
	fmt.Println(IntVal.Elem())
	c := IntVal.Elem().Int() + 1
	fmt.Println(c, IntVal.Elem())

	//setint需要sval的值是指针形式，这个时候除了用set设置值，只能转接口再断言设置,或者直接对接口参数断言修改值
	fmt.Println("IntVal.Elem()是否可以被修改:", IntVal.Elem().CanSet())
	IntVal.Elem().SetInt(20)
	fmt.Println(c, IntVal.Elem())
	//转接口断言再设置，也可以直接对传入的接口参数断言然后修改值
	if v, ok := (IntVal.Interface()).(*int); ok {
		*v++
		fmt.Println(c, IntVal.Elem())
	}

	//测试引用数据类型可以用setxxx函数不,map没有set函数，断言函数只能对接口类型使用
	map1 := map[int]string{1: "jumao"}
	fmt.Println(map1)
	mapVal := reflect.ValueOf(map1)
	fmt.Println(mapVal.Kind())
	fmt.Println("mapVal是否可以被修改:", mapVal.CanSet())
	if v, ok := (mapVal.Interface()).(map[int]string); ok {
		fmt.Println(v)
	}
}
